import React, { useEffect, useMemo, useState } from 'react';

import { Tree, TreeSelect as AntTreeSelect } from 'antd';
import { debounce } from 'lodash';
import classNames from 'classnames';
import PropTypes from 'prop-types';

import { useControllableValue } from '@/hooks';
import { HighlightText } from '@/molecules';
import { array2Tree, filterTreeArrayData } from '@/utils/tree';

import { getOptionItemByValue } from './util';
import styles from './index.less';

const { TreeNode } = Tree;

/**
 * 组织机构综合选择器
 * TODO treeCheckStrictly: true 子节点联动选中还没有实现
 */
const TreeSelect = React.forwardRef((props, ref) => {
    const {
        value: _value,
        onChange: _onChange,
        disabled,
        data,
        loading,
        multiple,
        showSearch,
        placeholder = '请选择',
        className,
        style,
        optionKeyProp = 'id',
        optionValueProp = 'id',
        optionLabelProp = 'title',
        optionPidProp = 'pId',
        renderTreeNodeTitle,
        ...otherProps
    } = props;
    const [value, onChange] = useControllableValue(props);
    const treeData = useMemo(() => array2Tree(data), [data]);

    // 如果有过滤数据, 需要展开过滤的数据
    const [expandedKeys, setExpandedKeys] = useState([]);

    // 搜索关键字
    const [filterKeyword, setFilterKeyword] = useState('');
    // 关键字防抖动
    const setFilterKeywordDebounce = useMemo(() => debounce(setFilterKeyword, 500), []);

    // 避免频繁触发数据过滤
    useEffect(() => {
        // 如果有过滤条件, 就执行过滤
        if (filterKeyword) {
            // 需要保留查找节点的父级, 所以使用 filterTreeArrayData
            const result = filterTreeArrayData(
                data,
                item => item[optionLabelProp].includes(filterKeyword),
                optionKeyProp,
                optionPidProp
            );
            setExpandedKeys(result.map(item => item[optionKeyProp]));
        }
    }, [data, filterKeyword, optionKeyProp, optionLabelProp, optionPidProp]);

    const handleTreeNodeClick = node => {
        if (node.selectable === false) {
            if (expandedKeys.includes(node[optionKeyProp])) {
                setExpandedKeys([...expandedKeys.filter(item => item !== node[optionKeyProp])]);
            } else {
                setExpandedKeys([...expandedKeys, node[optionKeyProp]]);
            }
        }
    };

    // 渲染树节点
    const renderTreeNode = currentTreeData => {
        const getTitle = node =>
            renderTreeNodeTitle ? (
                renderTreeNodeTitle(node, filterKeyword)
            ) : (
                <>
                    <HighlightText keywords={filterKeyword}>{node[optionLabelProp]}</HighlightText>
                    {node.statistics !== null && node.statistics !== undefined ? `(${node.statistics})` : ''}
                </>
            );
        return currentTreeData.map(node => (
            <TreeNode
                title={<div onClick={() => handleTreeNodeClick(node)}>{getTitle(node)}</div>}
                pureTitle={node[optionLabelProp]}
                value={node[optionValueProp]}
                key={node[optionKeyProp]}
                selectable={!(node?.disabled === true) && node.isLeaf !== false}
                disabled={node?.disabled === true}
                isLeaf={node.isLeaf === true ? true : node.isLeaf}
            >
                {node.children ? renderTreeNode(node.children) : null}
            </TreeNode>
        ));
    };

    const treeProps = {};
    if (multiple) {
        treeProps.onChange = (val, nodes) => {
            onChange(val, getOptionItemByValue(data, val, optionKeyProp), nodes);
        };
        treeProps.treeCheckable = true;
        treeProps.multiple = true;
    } else {
        treeProps.onSelect = (val, node) => {
            if (node.isLeaf) {
                onChange(val, getOptionItemByValue(data, val, optionKeyProp), node);
            }
        };
    }

    return (
        <AntTreeSelect
            ref={ref}
            disabled={disabled}
            value={loading ? '' : value}
            style={{ width: '100%', ...style }}
            className={classNames(styles.treeSelect, className)}
            showSearch={showSearch}
            allowClear
            placeholder={placeholder}
            searchPlaceholder="请输入关键字"
            treeNodeFilterProp="pureTitle"
            treeNodeLabelProp="pureTitle"
            treeExpandedKeys={expandedKeys}
            onSearch={setFilterKeywordDebounce}
            onTreeExpand={setExpandedKeys}
            dropdownStyle={{ maxHeight: 304, overflow: 'auto' }}
            getPopupContainer={trigger => trigger.parentNode}
            {...treeProps}
            {...otherProps}
        >
            {renderTreeNode(treeData)}
        </AntTreeSelect>
    );
});

TreeSelect.propTypes = {
    placeholder: PropTypes.string,
    /** 是否显示搜索 */
    showSearch: PropTypes.bool,
    /** 是否是多选 */
    multiple: PropTypes.bool,
    /** 是否禁用组件 */
    disabled: PropTypes.bool,
    /** 指定数据的 key 值对应字段, 默认是 id */
    optionKeyProp: PropTypes.string,
    /** 指定数据的 value 值对应字段, 默认是 id */
    optionValueProp: PropTypes.string,
    /** 指定数据的 title 值对应字段, 默认是 title */
    optionLabelProp: PropTypes.string,
    /** 指定数据的 pId 值对应字段, 默认是 pId */
    optionPidProp: PropTypes.string,
    /** 可选的最大数量 */
    max: PropTypes.oneOfType([PropTypes.number, PropTypes.bool]),
    /** 自定义标题的渲染 */
    renderTreeNodeTitle: PropTypes.func,
    /** 树节点选择策略是否是严格模式, 默认是 false */
    treeCheckStrictly: PropTypes.bool
};
TreeSelect.defaultProps = {
    placeholder: '请选择',
    showSearch: true,
    multiple: false,
    disabled: false,
    optionKeyProp: 'id',
    optionValueProp: 'id',
    optionLabelProp: 'title',
    optionPidProp: 'pId',
    max: false,
    renderTreeNodeTitle: null,
    treeCheckStrictly: false
};
TreeSelect.displayName = 'TreeSelect';

export default TreeSelect;
